﻿--[[
Name: LibAvion-2.0
Revision: @project-revision@
Author(s): Sirow (EU-Gorgonnash)
Website: http://code.google.com/p/sirow/
Description: A library that triggers visual effects.
Dependencies: LibStub, LibSimpleTimer-1.0
]]


-------------------------------------------------------------------------------
-- Library - Version Info & Dependencies
-------------------------------------------------------------------------------
-- Version
local MAJOR 		= "LibAvion-2.0"
local MINOR 		= tonumber(("@project-revision@"):match("%d+")) or 0

-- Dependencies
if not LibStub 						  then error(MAJOR .. " requires LibStub.") end
if not LibStub("LibSimpleTimer-1.0")  then error(MAJOR .. " requires LibSimpleTimer-1.0.") end


-------------------------------------------------------------------------------
-- Library - Register
-------------------------------------------------------------------------------
-- Register
local LibAvion 	= LibStub:NewLibrary(MAJOR, MINOR)
if not LibAvion then return end

-- Embed CallbackHandler and LibSimpleTimer
LibStub("LibSimpleTimer-1.0"):Embed(LibAvion)


-------------------------------------------------------------------------------
-- Library - Variables
-------------------------------------------------------------------------------
-- Set embeeding variables
LibAvion.mixinTargets = LibAvion.mixinTargets or {}
local mixins = {
	"TriggerAnimation",
	"StopAnimation",
	"StopAllAnimations",
	"IsAnimationRunning",
	
	"ScheduleTimer", 
	"ScheduleRepeatingTimer", 
	"IsTimerScheduled", 
	"CancelTimer",
	
	"animationCache",
}

-- Common
_G.LibAvion__updateInterval 	= 0.05
local updateInterval			= _G.LibAvion__updateInterval

-- Cache for animations/guides
LibAvion.animationCache	= LibAvion.animationCache 	or {}
LibAvion.caches			= LibAvion.caches 			or {
	animationFrames 	= {},
	animationCounter	= 0,
}
local animationCache	= LibAvion.animationCache
local animationFrames	= LibAvion.caches.animationFrames
local animationCounter	= LibAvion.caches.animationCounter


-------------------------------------------------------------------------------
-- Library - Animation - Core
-------------------------------------------------------------------------------
-- Create a new frame
local function MakeFrame()
	-- Name of the frame
	local frameName = "Avion_Frame_" .. animationCounter

	-- Create a new frame and apply some basic settings
	local textureFrame = CreateFrame("FRAME", frameName, UIParent)
	textureFrame:Hide()
	textureFrame:SetPoint("CENTER", UIParent, "CENTER")
	textureFrame:SetBackdropColor(0, 0, 0, 0)
	textureFrame:SetFrameStrata("BACKGROUND")
	textureFrame:SetScale(1)
	
	-- Add a texture to the frame (Blend mode = additive)
	local texture = textureFrame:CreateTexture(frameName .. "_Texture", "ARTWORK")
	texture:SetParent(textureFrame)
	texture:SetAllPoints(textureFrame)
	texture:SetBlendMode("ADD")
	texture:SetTexCoordModifiesRect(false)
	
	-- Add a font to the frame
	local fontFrame = CreateFrame("FRAME", frameName .. "_fontFrame")
	fontFrame:Hide()
	fontFrame:SetAllPoints(textureFrame)
	fontFrame:SetScale(1 / 1.414)
	
	local font = fontFrame:CreateFontString(frameName .. "_Font", "OVERLAY")
	font:SetParent(fontFrame)
	font:SetPoint("CENTER", fontFrame ,"CENTER", 0, 0)
	font:SetJustifyH("CENTER")
	font:SetJustifyV("MIDDLE")
	
	-- Add everything to the cache
	animationFrames[animationCounter] = {
		inUse 			= true,
		textureFrame 	= textureFrame,
		texture 		= texture,
		fontFrame 		= fontFrame,
		font 			= font,
		elapsedTimes 	= 0,
		randomNess 		= {
			rotation		= 0,
			position		= {
				x 				= 0,
				y				= 0,
			},
			color			= {
								r = 0,
								g = 0,
								b = 0,
			},
			alpha			= 0,
			scale			= 0,
		},
	}
	
	-- Increment number of frames and return new number
	animationCounter = animationCounter + 1
	return (animationCounter-1)
end


-- Get an unused frame or create a new one (only ID)
local function GetAvailableFrameID()
	-- Is there any unused/invisible frame?
	for index, cache in pairs(animationFrames) do
		if (not cache.inUse) then
			-- Enable inUse and return index
			cache.inUse 		= true
			cache.elapsedTimes 	= 0
			
			return index
		end
	end
	
	-- If not, create one!
	return MakeFrame()
end


-- Get an unused frame or create a new one
local function GetAvailableFrame()
	local frameID = GetAvailableFrameID()
	return animationFrames[frameID], frameID
end


-- Update values with linear interporlation between start and end
local function Interpolation(startValues, endValues, param) 
	return startValues + (endValues - startValues) * param
end


-- Will rotate texture (and shrink it by a factor of sqrt(2)) and mirror at X/Y axis if needed
local function RotateAndMirror(texture, angle, mirrorX, mirrorY)
	--[[
	NOTE 1: 
	You want to rotate the rectangle (0,0), (1,0), (1,1), (0,1) arround the Point (0.5, 0.5).
	The normal 2D-Rotationsmatrix
	R = [cos(a) -sin(a); sin(a) cos(a)] (where ; means new row)
	will rotate everything around the origin point (0,0).
	Thus we need to do the following:
	Translate our desired rotation-point (0.5,0.5) into the origin by
	T = [1 0 -0.5; 0 1 -0.5; 0 0 1] (in homogenous coords, rougly meaning [x y] will get [x y 1])
	Now we can use the above R (also in homogenous coords) to rotate around the origin and then use
	T^-1 = [1 0 0.5; 0 1 0.5; 0 0 1] 
	to move everything back to were it belongs.
	Given our texture coords [x y]_texture this would lead to:
	[x y 1]_transformed = [1 0 0.5; 0 1 0.5; 0 0 1]  * [cos(a) -sin(a) 0; sin(a) cos(a) 0; 0 0 1]  * [1 0 -0.5; 0 1 -0.5; 0 0 1] *[x y 1]_texture
	which (in short) can be seen below.
	
	NOTE 2:
	There is just one tiny problem, after each rotation the coordibates still have to be
	inside the rectangle (0,0), (1,0), (1,1), (0,1)  or some parts of the texture won't be
	displayed. Thus, the all texture coords (texture NOT frame) are "scaled down"
	by a factor of sqrt(2) (which is, after pytagoras the length of the recangles diagonal).
	Yet scaling is also done around the origin, not (0.5, 0.5), thus we used the exact same
	procedure as above, only replacing R with
	S = [1/sqrt 0 0; 0 1/sqrt(2) 0; 0 0 1]
	leaving:
	[x y 1]_transformed = [1 0 0.5; 0 1 0.5; 0 0 1]  * [1/sqrt 0 0; 0 1/sqrt(2) 0; 0 0 1]  * [1 0 -0.5; 0 1 -0.5; 0 0 1] *[x y 1]_texture
	
	NOTE 3:
	Mirroring is done by
	M_x = [-1 0 0; 0 1 0; 0 0 1]
	M_y = [1 0 0; 0 -1 0; 0 0 1]
	Some rules as above apply to mirroring. Need to mirror around X=0.5 or Y=0.5 axis, but those matrizes only
	mirror around X- or Y-axis, leading to:
	[x y1]_transformed = [1 0 0.5; 0 1 0.5; 0 0 1]  * M_x * [1 0 -0.5; 0 1 -0.5; 0 0 1] *[x y 1]_texture (X Axis)
	[x y1]_transformed = [1 0 0.5; 0 1 0.5; 0 0 1]  * M_y * [1 0 -0.5; 0 1 -0.5; 0 0 1] *[x y 1]_texture (Y Axis)
	[x y1]_transformed = [1 0 0.5; 0 1 0.5; 0 0 1]  * M_x * M_y * [1 0 -0.5; 0 1 -0.5; 0 0 1] *[x y 1]_texture (X=Y Axis)
	
	NOTE 4:
	For final result, all transformations need to be combined (in the right order, since matrix multiplication
	is commonly not kummulativ):
	[x y1]_transformed 
	= [1 0 0.5; 0 1 0.5; 0 0 1]  * [1/sqrt 0 0; 0 1/sqrt(2) 0; 0 0 1]  * [1 0 -0.5; 0 1 -0.5; 0 0 1]  (scale)
	* [1 0 0.5; 0 1 0.5; 0 0 1]  * [cos(a) -sin(a) 0; sin(a) cos(a) 0; 0 0 1]  * [1 0 -0.5; 0 1 -0.5; 0 0 1]  (rotate)
	* [1 0 0.5; 0 1 0.5; 0 0 1]  * M_xORy * [1 0 -0.5; 0 1 -0.5; 0 0 1]  (mirror)
	* [x y 1]_texture
	Which is equal to:
	x y1]_transformed 
	= [1 0 0.5; 0 1 0.5; 0 0 1]  (move back from origin)
	* [1/sqrt 0 0; 0 1/sqrt(2) 0; 0 0 1]  (scale)
	* [cos(a) -sin(a) 0; sin(a) cos(a) 0; 0 0 1]  (rotate)
	* M_xORy (mirror)
	* [1 0 -0.5; 0 1 -0.5; 0 0 1]  (move to origin)
	* [x y 1]_texture
	Apply this transformations to all edges of the (0,0), (1,0), (1,1), (0,1) rectangle, you SHOULD
	get the values you see below. (0.5 * sqrt(2) = 1/sqrt(2) ~ 0.7072)
	
	NOTE 5:
	Remember that texture origin is at top-left of frame. Meaning x goes right, y goes down.
	]]
	
	-- Mirror
	local mX, mY = 1, 1
	if (mirrorX) then
		mX = -1
	end
	if (mirrorY) then
		mY = -1
	end
	
	-- Rotation
	local cosAlpha = 0.7072 * math.cos(angle)
	local sinAlpha = 0.7072 * math.sin(angle)
	texture:SetTexCoord(
		-cosAlpha*mY + sinAlpha*mX + 0.5, -sinAlpha*mY - cosAlpha*mX + 0.5, 
		-cosAlpha*mY - sinAlpha*mX + 0.5, -sinAlpha*mY + cosAlpha*mX + 0.5, 
		 cosAlpha*mY + sinAlpha*mX + 0.5,  sinAlpha*mY - cosAlpha*mX + 0.5, 
		 cosAlpha*mY - sinAlpha*mX + 0.5,  sinAlpha*mY + cosAlpha*mX + 0.5
	)
end


-- Initialize frame settings and show it
local function InitFrame(object, init, misc, text)
	-- (Re)set texture file and color
	object.texture:SetTexture(misc.textureFile, false)
	object.texture:SetVertexColor(init.color.r, init.color.g, init.color.b)
	
	-- Adjust frame size, position and alpha
	object.textureFrame:SetHeight(misc.size.height * 1.4142 * init.scale)
	object.textureFrame:SetWidth(misc.size.width * 1.4142 * init.scale)
	object.textureFrame:SetPoint("CENTER", UIParent, "CENTER", init.position.x, init.position.y)
	object.textureFrame:SetAlpha(init.alpha)
	
	-- Rotation
	RotateAndMirror(object.texture, init.rotation, misc.mirror.x, misc.mirror.y)
	
	-- Set text
	if (text) then
		-- Position
		if (text.isAnimated.position) then
			object.font:SetPoint("CENTER", object.textureFrame, "CENTER", text.position.x, text.position.y)
		else
			object.font:SetPoint("CENTER", UIParent, "CENTER", text.position.x, text.position.y)
		end
		
		--Font typ and size
		object.font:SetFont(text.font, text.size, text.outline and "OUTLINE" or nil)
		
		-- Font color and alpha
		if (text.isAnimated.color) then
			object.font:SetTextColor(init.color.r, init.color.g, init.color.b, 1.0)
		else
			object.font:SetTextColor(text.color.r, text.color.g, text.color.b, 1.0)
		end
		
		-- Alpha
		object.fontFrame:SetAlpha(text.isAnimated.alpha and init.alpha or text.alpha)
		
		-- Display text
		object.font:SetText(text.text)
		
		-- Finally show it
		object.fontFrame:Show()
	end
	
	-- Finally show it
	object.textureFrame:Show()
end


-- Actually update frame animation
local function AnimateFrame(object, init, anim, animationFunc, addRandom, isAnimated, size, mirror, text, param, ignoreAlpha)
	-- Randomness
	local randomNess = object.randomNess

	-- Get and set updated values - Alpha
	if (isAnimated.alpha and not ignoreAlpha) then
		-- Interpolate
		local alpha = Interpolation(
			init.alpha, 
			anim.alpha,
			animationFunc.alpha(param)
		)
		
		-- Add randmness?
		if (addRandom and addRandom.alpha and addRandom.alpha > 0) then
			randomNess.alpha = 0.5 * randomNess.alpha + 0.5 * (math.random(-100, 100) / 100 * addRandom.alpha)
			
			object.textureFrame:SetAlpha(alpha + randomNess.alpha)
			if (text.isAnimated.alpha) then
				object.fontFrame:SetAlpha(alpha + randomNess.alpha)
			end
		else
			object.textureFrame:SetAlpha(alpha)
			if (text.isAnimated.alpha) then
				object.fontFrame:SetAlpha(alpha)
			end
		end
	end	
	
	-- Get and set updated values - Color
	if (isAnimated.color) then
		-- Interpolate
		local color = {
			r = Interpolation(
				init.color.r, 
				anim.color.r, 
				animationFunc.color(param)
			),
			g = Interpolation(
				init.color.g, 
				anim.color.g, 
				animationFunc.color(param)
			),
			b = Interpolation(
				init.color.b, 
				anim.color.b, 
				animationFunc.color(param)
			)
		}
	
		-- Add randmness?
		if (addRandom and addRandom.color and addRandom.color > 0) then
			randomNess.color.r = 0.5 * randomNess.color.r + 0.5 * (math.random(-100, 100) / 100 * addRandom.color)
			randomNess.color.g = 0.5 * randomNess.color.g + 0.5 * (math.random(-100, 100) / 100 * addRandom.color)
			randomNess.color.b = 0.5 * randomNess.color.b + 0.5 * (math.random(-100, 100) / 100 * addRandom.color)
			
			object.texture:SetVertexColor(color.r + randomNess.color.r, color.g + randomNess.color.g, color.b + randomNess.color.b)
			if (text.isAnimated.color) then
				object.font:SetTextColor(color.r + randomNess.color.r, color.g + randomNess.color.g, color.b + randomNess.color.b, 1.0)
			end
		else
			object.texture:SetVertexColor(color.r, color.g, color.b)
			if (text.isAnimated.color) then
				object.font:SetTextColor(color.r, color.g, color.b, 1.0)
			end
		end
	end
	
	-- Get and set updated values - Position
	if (isAnimated.position) then
		-- Interpolate
		local position = {
			x = Interpolation(
				init.position.x, 
				anim.position.x, 
				animationFunc.position(param)
			),
			y = Interpolation(
				init.position.y, 
				anim.position.y, 
				animationFunc.position(param)
			)
		}
		
		-- Add randmness?
		if (addRandom and addRandom.position and addRandom.position > 0) then
			randomNess.position.x = 0.5 * randomNess.position.x + 0.5 * (math.random(-100, 100) / 100 * addRandom.position)
			randomNess.position.y = 0.5 * randomNess.position.y + 0.5 * (math.random(-100, 100) / 100 * addRandom.position)
			
			object.textureFrame:SetPoint("CENTER", UIParent, "CENTER", position.x + randomNess.position.x, position.y + randomNess.position.y)
		else
			object.textureFrame:SetPoint("CENTER", UIParent, "CENTER", position.x, position.y)
		end
		
		
	end
	
	-- Get and set updated values - Scale
	if (isAnimated.scale) then
		-- Interpolate
		local scale = Interpolation(
			init.scale,
			anim.scale,
			animationFunc.scale(param)
		)
		
		-- Add randmness?
		if (addRandom and addRandom.scale and addRandom.scale > 0) then
			randomNess.scale = 0.5 * randomNess.scale + 0.5 * (math.random(-100, 100) / 100 * addRandom.scale)
			
			object.textureFrame:SetHeight(size.height * 1.4142 * (scale + randomNess.scale))
			object.textureFrame:SetWidth(size.width   * 1.4142 * (scale + randomNess.scale))
			
			-- Update text...
			if (text) then
				-- ...Position
				if (text.isAnimated.position) then
					object.font:SetPoint("CENTER", object.textureFramet, "CENTER", text.position.x * (scale + randomNess.scale), text.position.y * (scale + randomNess.scale))
				end
				
				-- ..Scale
				if (text.isAnimated.scale) then
					object.font:SetFont(text.font, text.size * (scale + randomNess.scale), text.outline and "OUTLINE" or nil)
				end
			end
		else
			object.textureFrame:SetHeight(size.height * 1.4142 * scale)
			object.textureFrame:SetWidth(size.width   * 1.4142 * scale)
			
			-- Update text
			if (text) then
				-- ...Position
				if (text.isAnimated.position) then
					object.font:SetPoint("CENTER", object.textureFrame, "CENTER", text.position.x * scale, text.position.y * scale)
				end
				
				-- ..Scale
				if (text.isAnimated.scale) then
					object.font:SetFont(text.font, text.size * scale, text.outline and "OUTLINE" or nil)
				end
			end
		end
	end
	
	-- Get and set updated values - Rotation
	if (isAnimated.rotation) then
		-- Interpolate
		local angle = Interpolation(
			init.rotation, 
			anim.rotation,
			animationFunc.rotation(param)
		)
	
		-- Add randmness?
		if (addRandom and addRandom.rotation and addRandom.rotation > 0) then
			randomNess.rotation = 0.7 * randomNess.rotation + 0.3 * (math.random(-100, 100) / 100 * addRandom.rotation / 180 * 3.1415)
			
			RotateAndMirror(object.texture, angle + randomNess.rotation, mirror.x, mirror.y)
		else
			RotateAndMirror(object.texture, angle, mirror.x, mirror.y)
		end
	end	
end


-- Update frame animation
local function UpdateAnimation(object, animationTable, fadeOutNow, forceStatus)
	-- Update elapsed time (time since animation was triggered)
	local elapsedTime = object.elapsedTimes * updateInterval
	object.elapsedTimes = object.elapsedTimes + 1

	-- Parameter for update function
	local param
	
	-- For options only
	if (forceStatus ~= false) then
		if (forceStatus == 0) then
			AnimateFrame(object, animationTable.init, animationTable.anim, animationTable.anim.misc.animationFunc, nil, animationTable.anim.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, 0)
		elseif (forceStatus == 1) then
			AnimateFrame(object, animationTable.init, animationTable.anim, animationTable.anim.misc.animationFunc, nil, animationTable.anim.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, 1)
		elseif (forceStatus == 2) then
			-- Fade and continue animation
			if (animationTable.misc.fadeMode == 1) then
				AnimateFrame(object, animationTable.init, animationTable.anim, animationTable.anim.misc.animationFunc, nil, animationTable.anim.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, 1)
			-- Inverting animation
			elseif (animationTable.misc.fadeMode == 2) then
				AnimateFrame(object, animationTable.init, animationTable.anim, animationTable.anim.misc.animationFunc, nil, animationTable.anim.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, 0)
			-- Use fade settings
			elseif (animationTable.misc.fadeMode == 3) then
				AnimateFrame(object, animationTable.anim, animationTable.fade, animationTable.fade.misc.animationFunc, nil, animationTable.fade.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, 1)
			end
			
			-- Make visible
			object.textureFrame:SetAlpha(1.0)
			object.fontFrame:SetAlpha(1.0)
		end
	
	-- Normal animation
	else 
		-- Animate frame, move, resize, color, rotate, ect.
		if (elapsedTime <= animationTable.anim.misc.duration) then
			-- Calc parameter for linear interpolation (param \in [0...1]) and animate frame
			if (animationTable.misc.fadeMode == 1) then
				param = elapsedTime / (animationTable.anim.misc.duration + animationTable.fade.misc.duration)
			else
				param = elapsedTime / animationTable.anim.misc.duration
			end
			AnimateFrame(object, animationTable.init, animationTable.anim, animationTable.anim.misc.animationFunc, animationTable.anim.misc.addRandom, animationTable.anim.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, param)
			
		-- Stop doing anything to the frame
		elseif (animationTable.misc.noEnd and not fadeOutNow) then
			-- We just disabled animation, so no update to actual animation time
			object.elapsedTimes = object.elapsedTimes - 1
			
			-- Stop animation timer, since nothing to do
			if (LibAvion:IsTimerScheduled("Avion_UpdateAnimation_" .. object.triggerID)) then
				LibAvion:CancelTimer("Avion_UpdateAnimation_" .. object.triggerID)
			end
		
		-- Start fading frame
		elseif(elapsedTime - animationTable.anim.misc.duration <= animationTable.fade.misc.duration) then		
			-- Fade and continue animation
			if (animationTable.misc.fadeMode == 1) then
				param = elapsedTime / (animationTable.anim.misc.duration + animationTable.fade.misc.duration)
				AnimateFrame(object, animationTable.init, animationTable.anim, animationTable.anim.misc.animationFunc, animationTable.anim.misc.addRandom, animationTable.anim.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, param, true)

			-- Inverting animation
			elseif (animationTable.misc.fadeMode == 2) then
				param = 1 - (elapsedTime - animationTable.anim.misc.duration) / animationTable.fade.misc.duration
				AnimateFrame(object, animationTable.init, animationTable.anim, animationTable.anim.misc.animationFunc, animationTable.anim.misc.addRandom, animationTable.anim.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, param, true)
			
			-- Use fade settings
			elseif (animationTable.misc.fadeMode == 3) then
				param = (elapsedTime - animationTable.anim.misc.duration) / animationTable.fade.misc.duration
				AnimateFrame(object, animationTable.anim, animationTable.fade, animationTable.fade.misc.animationFunc, animationTable.fade.misc.addRandom, animationTable.fade.misc.isAnimated, animationTable.misc.size, animationTable.misc.mirror, animationTable.text, param, true)
				
			end
			
			-- Current timing
			local param2
			param = (elapsedTime - animationTable.anim.misc.duration) / animationTable.fade.misc.duration
			
			-- Just fade
			if (animationTable.fadeMode == 0) then
				-- Use animation alpha?
				if (animationTable.anim.misc.isAnimated.alpha) then
					param2 = (1-param) * animationTable.anim.alpha
				else
					param2 = (1-param)
				end
			-- Fade with animation or invert
			elseif (animationTable.fadeMode == 1 or animationTable.fadeMode == 2) then
				-- Use animation alpha?
				if (animationTable.anim.misc.isAnimated.alpha) then
					param2 = Interpolation(
						animationTable.anim.alpha, 
						0.0,
						animationTable.anim.misc.animationFunc.alpha(param)
					)
				else
					param2 = Interpolation(
						1.0, 
						0.0,
						animationTable.anim.misc.animationFunc.alpha(param)
					)
				end
			-- Use fade settings
			else
				-- Use animation alpha?
				if (animationTable.anim.misc.isAnimated.alpha) then
					param2 = Interpolation(
						animationTable.anim.alpha, 
						0.0,
						animationTable.fade.misc.animationFunc.alpha(param)
					)
				else
					param2 = Interpolation(
						1.0, 
						0.0,
						animationTable.fade.misc.animationFunc.alpha(param)
					)
				end
			end
			
			-- Update alpha (frame and text)
			object.textureFrame:SetAlpha(param2)
			if (animationTable.text) then
				if (animationTable.text.isAnimated.alpha) then
					object.fontFrame:SetAlpha(param2)
				else
					object.fontFrame:SetAlpha((1-param) * animationTable.text.alpha)
				end
			end
			
		-- Done, Unregister
		else
			LibAvion:StopAnimation(object.triggerID, false)
		end
	end
end


-- "Register" a new frame for animation (this registration handels timing)
local function StartAnimationTimer(object, animationTable, updatedAnimationTime, updatedWaitTime)
	local self = LibAvion
	
	-- If there is NO wait-time, initialize and run animation
	if (updatedWaitTime == 0) then
		-- Initialize frame stuff
		if (not object.textureFrame:IsVisible()) then
			InitFrame(object, animationTable.init, animationTable.misc, animationTable.text)
		end
		
		-- We only need to test if something is animated if there is really animation-time ^^
		local somethingAnimated = false
		if (updatedAnimationTime ~= 0) then
			-- If there is nothing to animate, well ... life is easier ^^
			for _, value in pairs(animationTable.anim.misc.isAnimated) do
				if (value) then
					somethingAnimated = true
				end
			end
			for _, value in pairs(animationTable.fade.misc.isAnimated) do
				if (value) then
					somethingAnimated = true
				end
			end
		end
		
		-- Well ... thats life, go the hard way ^^
		if (somethingAnimated or updatedAnimationTime == 0) then
			-- Register animation
			LibAvion:ScheduleRepeatingTimer(
				"Avion_UpdateAnimation_" ..object.triggerID,
				UpdateAnimation,
				updateInterval,
				--self, 
				object, 
				animationTable,
				false,
				false
			)
		else
			-- Start animation after animationTime has passed, since there was nothing to animate...
			LibAvion:ScheduleTimer(
				"Avion_DisplayAnimation_" .. object.triggerID,
				StartAnimationTimer,
				animationTable.anim.misc.duration, 
				--self, 
				object, 
				animationTable, 
				0, 
				0
			)
		end
	-- If there IS wait-time, retrigger after waitTime-delay
	else
		-- Show frame allready?
		if (animationTable.misc.showWhileWait) then
			InitFrame(object, animationTable.init, animationTable.misc, animationTable.text)
		end
		
		-- Start waiting
		LibAvion:ScheduleTimer(
			"Avion_WaitAnimation_" .. object.triggerID,
			StartAnimationTimer,
			animationTable.init.misc.duration,  
			--self, 
			object, 
			animationTable, 
			animationTable.anim.misc.duration, 
			0
		)
	end
end


-------------------------------------------------------------------------------
-- Library - Animation - API
-------------------------------------------------------------------------------
--[[
######################################################
# :TriggerAnimation(triggerID, animationTable)
# Notes:
#	Start showing given (by animationTable) animation and associate it with identifier triggerID
# Arguments:
#	triggerID (string) - The unique identifier for this animation.
#	animationTable (table) - The table that defines this animation, see EOF for more.
# Returns:
######################################################
]]--
function LibAvion:TriggerAnimation(triggerID, animationTable)
	-- Quite when disabled or allready registered
	if (self:IsAnimationRunning(triggerID)) then
		return
	end
	
	-- Make things local
	local object, frameID
	object, frameID = GetAvailableFrame()
	
	-- Backup animation data (eg. needed when fading out)
	animationCache[triggerID] = {
		animationTable = animationTable,
		frameID = frameID
	}
	
	-- Save which triggerID triggerd this animation
	object.triggerID = triggerID
	
	-- Play soundfile if there is one to play
	if (animationTable.misc.soundFile) then
		PlaySoundFile(animationTable.misc.soundFile)
	end
	
	-- Finally "start" animation/timer
	StartAnimationTimer(object, animationTable, animationTable.anim.misc.duration, animationTable.init.misc.duration)
end


--[[
######################################################
# :StopAnimation(triggerID, fadeOut)
# Notes:
#	Stop the animation that is assigned to this identifier.
# Arguments:
#	triggerID (string) - The unique identifier for this animation.
#	fadeOut - (bool) - Wether or not to fade or just stop and hide animation.
# Returns:
######################################################
]]--
function LibAvion:StopAnimation(triggerID, fadeOut)
	-- Hmmm, well trying to unregister an unregister animation may be painful >:>
	if (self:IsAnimationRunning(triggerID)) then
		-- Unregister animation (delayed and repeating animations)
		if (self:IsTimerScheduled("Avion_UpdateAnimation_" .. triggerID)) then
			self:CancelTimer("Avion_UpdateAnimation_" .. triggerID)
		end
		if (self:IsTimerScheduled("Avion_DisplayAnimation_" .. triggerID)) then
			self:CancelTimer("Avion_DisplayAnimation_" .. triggerID)
		end
		if (self:IsTimerScheduled("Avion_WaitAnimation_" .. triggerID)) then
			self:CancelTimer("Avion_WaitAnimation_" .. triggerID)
		end
		
		-- Smoothly fade-out frame
		if (fadeOut) then
			-- Needed vars
			local object 			= animationFrames[animationCache[triggerID].frameID]
			local animationTable	= animationCache[triggerID].animationTable
			
			-- Adjust run-time of this animation to fit fading
			if (object.elapsedTimes * updateInterval - animationTable.anim.misc.duration < animationTable.fade.misc.duration) then
				object.elapsedTimes = animationTable.anim.misc.duration / updateInterval
			end
			
			-- Register for fading animation
			self:ScheduleRepeatingTimer(
				"Avion_UpdateAnimation_" .. triggerID,
				UpdateAnimation, 
				updateInterval,
				--self, 
				object, 
				animationTable,
				true,
				false
			)
		
		-- Stop and hide animation
		else
			-- Hide frame and its not inUse anymore...
			animationFrames[animationCache[triggerID].frameID].texture:SetTexture("Interface\\Icons\\Temp.blp")
			animationFrames[animationCache[triggerID].frameID].textureFrame:Hide()
			animationFrames[animationCache[triggerID].frameID].fontFrame:Hide()
			animationFrames[animationCache[triggerID].frameID].inUse = false
			
			-- Delete frame from animation cache
			animationCache[triggerID].animationTable 	= nil
			animationCache[triggerID] 					= nil
		end
	end
end


--[[
######################################################
# :StopAllAnimations(fadeOut)
# Notes:
#	Stops all running animations.
# Arguments:
#	fadeOut - (bool) - Wether or not to fade or just stop and hide animation.
# Returns:
######################################################
]]--
function LibAvion:StopAllAnimations(fadeOut)
	if (animationCache) then
		for triggerID, _ in pairs(animationCache) do
			self:StopAnimation(triggerID, fadeOut)
		end
	end
end


--[[
######################################################
# :IsAnimationRunning(triggerID)
# Notes:
#	Test if animation assosiated with this identifier is still active.
# Arguments:
#	triggerID (string) - The unique identifier for this animation.
# Returns:
#	true if its still active, false otherwise.
######################################################
]]--
function LibAvion:IsAnimationRunning(triggerID)
	if (animationCache and animationCache[triggerID]) then
		return true
	else
		return false
	end
end


--[[
######################################################
# :Embed(target)
# Notes:
#	Embeds methods listed in local <mixins> into target table.
# Arguments:
#	target (table) - The table on which to export methods onto.
# Returns:
#	The table provided, after embedding.
######################################################
]]--
function LibAvion:Embed(target)
	LibAvion.mixinTargets[target] = true
	
	for _, v in pairs(mixins) do
		target[v] = LibAvion[v]
	end
	
	return target
end


-- Reembed (eg. lib was updated)
for addon in pairs(LibAvion.mixinTargets) do
	LibAvion:Embed(addon)
end


-------------------------------------------------------------------------------
-- Library - Export
-------------------------------------------------------------------------------
_G.LibAvion__caches 			= LibAvion.caches
_G.LibAvion__GetAvailableFrame 	= GetAvailableFrame
_G.LibAvion__InitFrame			= InitFrame
_G.LibAvion__UpdateAnimation	= UpdateAnimation


-------------------------------------------------------------------------------
-- Dokumentation - Variables
-------------------------------------------------------------------------------
--[[
VAR: AnimationTable
TYPE: Table
DESC: It cantains all needed information to start an animation:
{
	init 			= {
		alpha 		= Alpha value when its shown (number)
		color 		= {
			r			= Red color when its shown (number)
			g 			= Green color when its shown (number)
			b 			= Blue color when its shown (number)
		}
		position 	= {
			x 			= X-Position on screen when its shown (number)
			y 			= Y-Position on screen when its shown (number)
		}
		scale		= Scale when its shown  (number)
		rotation 	= Rotation of texture when its shown (number)
		misc		= {
			duration	= The time it should take from triggering the animation until the actual animation should start. (number)
NOTE: Can be used to create difficult multi-animations.

		}
	}
	anim 		= {
		alpha 		= Alpha value that is beeing animated to (number)
		color 		= {
			r 			= Red color that is beeing animated to (number)
			g 			= Green color that is beeing animated to (number)
			b 			= Blue color that is beeing animated to (number)
		} 
		position 		= {
			x 				= X-Position that is beeing animated to (number)
			y 				= Y-Position that is beeing animated to (number)
		} 
		scale 			= Scale that is beeing animated to (number)
		rotation 		= Rotations of texture that is beeing animated to (number)
		misc			= {
			duration		= The time the "animation" phase should take (number)
			isAnimated 		= {
				alpha 			= Should visibility be animated? (bool)
				color 			= Should color be animated? (bool)
				position 		= Should position be animated? (bool)
				scale 			= Should scale be animated? (bool)
				rotation 		= Should rotation be animated? (bool)
			}
			addRandom 		= {
				alpha 			= Add some randomness to alpha animation (number)
				color 			= Add some randomness to color animation (number)
				position 		= Add some randomness to position animation (number)
				scale 			= Add some randomness to scale animation (number)
				rotation 		= Add some randomness to rotation animation (number)
			}
			animationFunc	= {
				alpha 			= Smoothness/speed of alpha animation (function)
				color 			= Smoothness/speed of color animation (function)
				position 		= Smoothness/speed of position animation (function)
				scale 			= Smoothness/speed of scale animation (function)
				rotation 		= Smoothness/speed of rotation animation (function)
			}
NOTE: All of this functions HAVE to be pointers to function with head 
"function (param)" where <param> is a value between 0 (start)
and 1 (end) depending on the time the animation has been running.

			animationFuncName	= {		
				alpha 				= Used for saving which function to use in animationFunc.alpha (string) [OPTIONAL]
				color 				= Used for saving which function to use in animationFunc.color (string) [OPTIONAL]
				position 			= Used for saving which function to use in animationFunc.position (string) [OPTIONAL]
				scale 				= Used for saving which function to use in animationFunc.scale (string) [OPTIONAL]
				rotation 			= Used for saving which function to use in animationFunc.rotation (string) [OPTIONAL]
			} [OPTIONAL]
NOTE: This is an optional setting read more about it in LibAvionOptions-2.0!

		}
	}
	fade 			= {
		color 			= {
			r 				= Red color that is beeing animated to when fading (number)
			g 				= Green color that is beeing animated to when fading (number)
			b 				= Blue color that is beeing animated to when fading (number)
		} 
		position 		= {
			x 				= X-Position that is beeing animated to when fading (number)
			y 				= Y-Position that is beeing animated to when fading (number)
		} 
		scale 			= Scale that is beeing animated to when fading (number)
		rotation 		= Rotations of texture that is beeing animated to when fading (number)
		misc			= {
			duration		= Duration of fading (number)
			isAnimated 		= {
				color 			= Should color be animated? (bool)
				position 		= Should position be animated? (bool)
				scale 			= Should scale be animated? (bool)
				rotation 		= Should rotation be animated? (bool)
			}
			addRandom 		= {
				alpha 			= Add some randomness to alpha animation (number)
				color 			= Add some randomness to color animation (number)
				position 		= Add some randomness to position animation (number)
				scale 			= Add some randomness to scale animation (number)
				rotation 		= Add some randomness to rotation animation (number)
			}
			animationFunc	= {
				alpha 			= Smoothness/speed of alpha animation (function)
				color 			= Smoothness/speed of color animation (function)
				position 		= Smoothness/speed of position animation (function)
				scale 			= Smoothness/speed of scale animation (function)
				rotation 		= Smoothness/speed of rotation animation (function)
			}
			animationFuncName	= {		
				alpha 			= Used for saving which function to use in animationFunc.alpha (string) [OPTIONAL]
				color 			= Used for saving which function to use in animationFunc.color (string) [OPTIONAL]
				position 		= Used for saving which function to use in animationFunc.position (string) [OPTIONAL]
				scale 			= Used for saving which function to use in animationFunc.scale (string) [OPTIONAL]
				rotation 		= Used for saving which function to use in animationFunc.rotation (string) [OPTIONAL]
			} [OPTIONAL]
		}
	}
	text			= {
		outline 		= Enable font outline (bool)
		font 			= Path (of file link no SharedMedia!) to the font (string)
		fontName 		= Name of the font as given by SharedMedia (string) [OPTIONAL]
		position	 	= {
			y 				= X-Offset relative to animation center
			x 				= Y-Offset relative to animation center
		}
		color 		= {
			r 			= Text color
			g 			= Text color
			b 			= Text color
		},
		alpha		= Text alpha
		isAnimated 	= {
			position	= Is text attached to animation?
			scale		= Is scale animated
			color		= Is color the same as animation (ignore color settings)
			alpha		= Is alpha animted (ignore alpha settings)
		}
		text 		= Text that is to be displayed.
NOTE:  If you want to use DogTag/LuaText than THIS is the formated/replaced text, do not put DogTag/LuaText in here. Instead
make play LuaText inside an extra var. and make sure "text" is set correctly prior to starting the animation!

		size 		= Font size
	} [OPTIONAL]
	misc 			= {
		size 			= {
			width 			= Width when its shown (number)
			height 			= Height when its shown (number)
		}

		fadeMode		= How to fade the frame
NOTE: 0 means just fading alpha; 1 means fade and continue animation 
2 means inverting animation when fading out; 3 means use fade settings

		showWhileWait 	= Allready show the frame/animation while "waitTime" is yet running (bool)
		textureFile 	= Texture file (file link no SharedMedia!) to the texture that should be used (string)
		textureFileName	= Used for saving which texture to use in textureFile (string) [OPTIONAL]		
NOTE: This is an optional setting read more about it in LibAvionOptions-2.0!

		soundFile 		= Sound file (file link no SharedMedia!) to the sound that is triggered as soon as the animation starts (string)
		soundFileName	= Name of the sound as given by SharedMedia (string) [OPTIONAL]
NOTE: This is an optional setting read more about it in LibAvionOptions-2.0!

		mirror 			= {
			x 				= Mirror texture on x-axis (bool)
			y 				= Mirror texture on y-axis (bool)
		}
		noEnd 			= The animation won't fade when its finished but will rather need to be stopped with LibAvion:StopAnimation(...) (bool)
	}
}


VAR: updateInterval
TYPE: Number
DESC: Update rate for all animations, eg. 0.05 means roughly 25 updates per second.


VAR: animationCache
TYPE: Table
DESC: Stores all currently active (running) animations, more speficly the unique key of the
frame (in animationFrames), mostly refered to as the FrameID that was assossiated to 
this animation. The frame thats beeing animated. Also saves (a link to)  the table that's used to
define the animation.
This table is accessed by the unique key, that was used to start the animation, namely triggerID.


VAR: animationFrames 
Type: Table
DESC: Table that contains a pool of created/available frame objects to be used for visualising
an animation. It has "animationCounter" number of entrys and the unique key that is
used to access it is called frameID. (or animationFrameID)
Each entry inhabits the following entrys:
	.inUse			- Say if the frame is currently beeing used for visualising an animation
	.textureFrame	- the actuell fram object that can be shown or hidden
	.texture		- the texture object
	.fontFrame		- Frame tat is attached to textureFrame but independantly reserved for font display
	.font			- Fontstring for text display
	.elapsedTimes	- iterater that (if the animation is currently running) tells how often it has been updated allready
	.randomNess		- will save last state/value of the random component (for position, scale, color, ect.) to be able to smooth randomness


VAR: animationCounter
TYPE: Number
DESC: Its just #animationFrames, nothing more, nothing less


EOF]]